import math
import copy
import logging
import numpy as np

from util.util_log import setup_logging
from util.util_ris_pattern import point_2_phase, phase_2_pattern, phase_2_pattern_xyz, eps, \
    phase_2_bit, phaseBit_2_deg, phase_2_pattern_xyz_fft
from util.util_analysis_plane import get_peaks, get_peak_nth


# 配置日志，默认打印到控制台，也可以设置打印到文件
setup_logging()
# setup_logging(log_file="../files/multi-beam-trace/beam2/QE/L=L1+L4/trace_2025-03-19-1644-16beam-qe.txt")
# 获取日志记录器并记录日志
logger = logging.getLogger("[FitnessMultiBeamTrace]")



# pattern 计算 peaks
def pattern_2_peaks(pattern):
    # pattern 转dB
    pattern_dbw = 20 * np.log10(np.abs(pattern) / np.max(np.max(np.abs(pattern))) + eps)
    # pattern找峰值
    peaks = get_peaks(pattern_dbw)
    return peaks


# 计算phase的方向图
def phaseBit_2_pattern(phaseBit, bit_num):
    phaseBitDeg = phaseBit_2_deg(phaseBit, bit_num)
    phaseBitRad = np.deg2rad(phaseBitDeg)
    # pattern_xyz, x, y, z = phase_2_pattern_xyz(phaseBitRad)
    pattern_xyz, x, y = phase_2_pattern_xyz_fft(phaseBitRad)  # FFT法计算, 快速
    # return np.abs(pattern_xyz), x, y
    return pattern_xyz, x, y



class FitnessMultiBeamTrace():
    __bit_num = 0  # 比特数
    __beam_num = 0  # 波束数

    def __init__(self, bit_num, beam_num):
        # 初始化阵列相关: 参数设置
        self.__bit_num = bit_num
        self.__beam_num = beam_num
        # 初始化遗传算法相关: fitness
        self.phaseBitTarget = 1
        self.phaseBitHistoryList = []   # 历史时刻的码阵, 从远到近
        self.fitness_l4_map = {}        # fitness - l4 约束的值与数量对应, eg: 1bit = {1: 512, 0: 512}
        self.main_lope_pos_list = []    # 记录主瓣位置

    def init_main_lope_pos(self, phaseBit_target):
        # 计算目标方向图的主瓣坐标, 保存到 self.main_lope_pos_list
        pattern, x, y = phaseBit_2_pattern(phaseBit_target, self.__bit_num)
        peaks = pattern_2_peaks(pattern)
        self.main_lope_pos_list = []
        for i in range(0, self.__beam_num):
            self.main_lope_pos_list.append(peaks[i][1])
        return self.main_lope_pos_list

    # 适应度: L0 -- 主瓣检查: 检查是否是最大主瓣位置
    def __fitness_l0_pos(self, selected_peaks):
        count = 0
        for peak in selected_peaks:
            peak_x = peak[1][0]
            peak_y = peak[1][1]
            for pos in self.main_lope_pos_list:
                pos_x = pos[0]
                pos_y = pos[1]
                if abs(peak_x - pos_x) <= 1 and abs(peak_y - pos_y) <= 1:
                    count += 1
                    break
        return count

    # 适应度: L0 -- 主瓣检查: 检查main_lope_pos位置是否小于3dB
    def __fitness_l0_3dB(self, pattern):
        count = 0
        pattern_3dB = np.max(pattern) / 2.0
        for pos in self.main_lope_pos_list:
            pos_x = pos[0]
            pos_y = pos[1]
            if pattern[pos_x][pos_y] > pattern_3dB:
                count += 1
        return count

    # 适应度: L0 -- 主瓣位置检测
    def fitness_l0(self, peaks, pattern):
        # 获取前 beam_num 个 peaks
        selected_peaks = peaks[:self.__beam_num]
        #
        # count = self.__fitnes_l0_pos(selected_peaks)    # 统计这些峰值在主瓣位置列表中的数量
        count = self.__fitness_l0_3dB(pattern)           # 统计主瓣位置是否小于3dB
        return np.abs(self.__beam_num - count)

    # 适应度: L1 -- PSLL 部分
    def fitness_l1(self, peaks):
        psll = 0
        # 找第beam_num+1峰(小于3dB)作为 PSLL
        if len(peaks) > self.__beam_num:
            peak_nth = get_peak_nth(peaks, self.__beam_num)
            if peak_nth is not None:
                psll = peak_nth[0]
        return psll

    # 适应度: L2 -- 相邻时刻码阵不同位置数量
    def fitness_l2(self, phaseBit):
        if self.phaseBitHistoryList is None or len(self.phaseBitHistoryList) == 0:
            return 0, 0
        phaseBitBefore = self.phaseBitHistoryList[-1]
        count = np.sum(phaseBit != phaseBitBefore)
        rows, cols = phaseBit.shape
        percentage = round((count / (rows * cols)) * 100, 4)
        return count, percentage

    # 适应度: L3 -- 历史码阵每个阵元工作时长均匀程度
    def fitness_l3(self, phaseBit):
        if self.phaseBitHistoryList is None or len(self.phaseBitHistoryList) == 0:
            return 0, 0, 0, 0, 0
        # 深拷贝 self.phaseBitHistoryList 到 phaseBit_all_list
        phaseBit_all_list = copy.deepcopy(self.phaseBitHistoryList)
        # 将 phaseBit 添加为最后一项
        phaseBit_all_list.append(phaseBit)
        # 统计 phaseBit_history_list 中每个位置的值等于 target 的次数
        count_target = sum(np.array(history == self.phaseBitTarget, dtype=int) for history in phaseBit_all_list)
        # 计算所有元素的平均值
        mean_value = np.mean(count_target)
        # 计算每个元素偏离平均值的平方差，并求和
        deviation_sum = np.sum((count_target - mean_value) ** 2)
        # 计算标准差（偏差平方和开方）
        standard_deviation = np.sqrt(deviation_sum)
        # 计算归一化的标准差 (基于最大值)
        normalized_std_max = standard_deviation / np.max(count_target)
        return count_target, mean_value, deviation_sum, standard_deviation, normalized_std_max

    # 适应度: L4 -- 一段时间内码阵功率为固定值: 可以是码阵为不同值的阵元数量在一段时间内相同
    def fitness_l4(self, phaseBit):
        # 将二维数组展平成一维数组
        flattened = phaseBit.flatten().astype(int)
        # 统计各个值的出现次数
        counts = np.bincount(flattened, minlength=len(self.fitness_l4_map))
        # 获取 fitness_l4_map 的值
        fitness_values = list(self.fitness_l4_map.values())
        # 检查 counts 和 fitness_values 的长度是否一致
        if len(counts) != len(fitness_values):
            print("Error: Length of counts and fitness_values do not match.")
            return counts, 0
        # 计算偏离对应值的方差
        variance = np.sum((counts - list(self.fitness_l4_map.values())) ** 2)
        return counts, int(variance)

    # 适应度: L5 -- 最少工作阵元, 稀疏布阵的推广
    def fitness_l5(self, phaseBit, target):
        # phaseBit是类型为np.array()的二维数组, 每个元素都是float。计算 phaseBit中值为 target 的元素数量
        # 计算 phaseBit 中等于 target 的元素数量
        # phaseBit 是一个 2D numpy 数组
        # target 是 float 类型
        # 使用 numpy 的高效计算方式
        equal_elements = np.count_nonzero(phaseBit == target)
        return equal_elements

    # 适应度: L = L1 + L2 + L3
    def fitness(self, phaseBit):
        # 前置计算, 计算pattern和peaks
        pattern, x, y = phaseBit_2_pattern(phaseBit, self.__bit_num)
        peaks = pattern_2_peaks(pattern)
        #
        # 计算 L0
        l0_main_lope_pos_miss = self.fitness_l0(peaks, pattern)
        # 计算 L1
        l1_psll = self.fitness_l1(peaks)
        # 计算 L2
        l2_count, l2_percentage = self.fitness_l2(phaseBit)
        # # 计算 L3
        # l3_count_target, l3_mean_value, l3_deviation_sum, l3_standard_deviation, l3_normalized_std_max \
        #     = self.fitness_l3(phaseBit)
        # 计算 L4
        # l4_counts, l4_variance = self.fitness_l4(phaseBit)
        # 计算 L5
        l5_counts = self.fitness_l5(phaseBit, 1)
        # 计算 L = L1 + L2 + L3
        # fitness = l1_psll + l2_percentage + l3_normalized_std_max
        # fitness = l1_psll + l2_percentage + l4_variance
        # fitness = l0_main_lope_pos_miss * 10000 + l1_psll * 100 + l2_percentage + l5_counts
        # fitness = 0.95 * math.exp(l0_main_lope_pos_miss) + l1_psll/50 * 0.65 + l2_percentage * 0.3 + l5_counts/4096 * 0.3
        # fitness = 100 * math.exp(
        #     l0_main_lope_pos_miss) + l1_psll / 50 * 50 + l2_percentage * 0.3 + l5_counts / 4096 * 0.3
        fitness = 10000 * math.exp(
            l0_main_lope_pos_miss) + l1_psll * 50 + l2_percentage + l5_counts
        # fitness = l1_psll + l2_percentage + l5_counts
        # fitness = l1_psll
        # logger.info("l=%f, l1=%f" % (fitness, l1_psll))
        logger.info("l=%f, l0=%f, l1=%f, l2_percentage=%f, l2_count=%f, l5_counts=%d"
                    % (fitness, l0_main_lope_pos_miss,l1_psll, l2_percentage, l2_count, l5_counts))
        # logger.info("l=%f, l1=%f, l2=%f, l3=%f" % (fitness, l1_psll, l2_percentage, l3_normalized_std_max))
        return fitness